/*-------------------------------------------------------------------------
 *
 * execQual.c--
 *    Routines to evaluate qualification and targetlist expressions
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/cvsroot/postgres95/src/backend/executor/execQual.c,v 1.10 1997/01/10 20:17:31 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 *   INTERFACE ROUTINES
 *    	ExecEvalExpr	- evaluate an expression and return a datum
 *    	ExecQual	- return true/false if qualification is satisified
 *    	ExecTargetList	- form a new tuple by projecting the given tuple
 *
 *   NOTES
 *	ExecEvalExpr() and ExecEvalVar() are hotspots.  making these faster
 *	will speed up the entire system.  Unfortunately they are currently
 *	implemented recursively..  Eliminating the recursion is bound to
 *	improve the speed of the executor.
 *
 *	ExecTargetList() is used to make tuple projections.  Rather then
 *	trying to speed it up, the execution plan should be pre-processed
 *	to facilitate attribute sharing between nodes wherever possible,
 *	instead of doing needless copying.  -cim 5/31/91
 *
 */
#include <string.h>

#include "postgres.h"
#include "fmgr.h"

#include "nodes/primnodes.h"
#include "nodes/relation.h"

#include "optimizer/clauses.h"

#include "nodes/memnodes.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/execdebug.h"
#include "executor/execFlatten.h"
#include "executor/functions.h"
#include "access/heapam.h"
#include "utils/memutils.h"
#include "utils/builtins.h"
#include "utils/palloc.h"
#include "utils/fcache.h"
#include "utils/fcache2.h"
#include "utils/array.h"
#include "utils/mcxt.h"

/* ----------------
 *	externs and constants
 * ----------------
 */

/*
 * XXX Used so we can get rid of use of Const nodes in the executor.
 * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
 * and by ExecEvalArrayRef.
 */
bool 	execConstByVal;
int 	execConstLen;

/* static functions decls */
static Datum ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull);
static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
			      bool *isNull, bool *isDone);

/* --------------------------------
 *    ExecEvalArrayRef
 *
 *     This function takes an ArrayRef and returns a Const Node if it
 *     is an array reference or returns the changed Array Node if it is
 *	   an array assignment.
 *
 * --------------------------------
 */
static Datum
ExecEvalArrayRef(ArrayRef *arrayRef,
		 ExprContext *econtext,
		 bool *isNull,
		 bool *isDone)
{
    bool	dummy;
    int 	i = 0, j = 0;
    ArrayType 	*array_scanner;
    List   	*upperIndexpr, *lowerIndexpr;
    Node	*assgnexpr;
    List	*elt;
    IntArray 	upper, lower;
    int 	*lIndex;
    char *dataPtr;
    
    execConstByVal = arrayRef->refelembyval;
    *isNull       =	false; 
    array_scanner =	(ArrayType*)ExecEvalExpr(arrayRef->refexpr,
					     econtext,
					     isNull,
					     isDone);
    if (*isNull) return (Datum)NULL;
    
    upperIndexpr = arrayRef->refupperindexpr;

    foreach (elt, upperIndexpr) {
    	upper.indx[i++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
					      econtext,
					      isNull,
					      &dummy);
    	if (*isNull) return (Datum)NULL;
    }
    
    lowerIndexpr = arrayRef->reflowerindexpr;
    lIndex = NULL;
    if (lowerIndexpr != NIL) {
	foreach (elt, lowerIndexpr) {
	    lower.indx[j++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
						  econtext,
						  isNull,
						  &dummy);
	    if (*isNull) return (Datum)NULL;
	}
	if (i != j)
	    elog(WARN, 
		 "ExecEvalArrayRef: upper and lower indices mismatch");
	lIndex = lower.indx;
    }

    assgnexpr    = arrayRef->refassgnexpr;
    if (assgnexpr != NULL) {
	dataPtr = (char*)ExecEvalExpr((Node *)
				      assgnexpr, econtext,
				      isNull, &dummy);
	if (*isNull) return (Datum)NULL;
	if (lIndex == NULL)
	    return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, 
				     arrayRef->refelembyval,
				     arrayRef->refelemlength,
				     arrayRef->refattrlength, isNull);
	return (Datum) array_assgn(array_scanner, i, upper.indx,
				   lower.indx, 
				   (ArrayType*)dataPtr, 
				   arrayRef->refelembyval,
				   arrayRef->refelemlength, isNull);
    }
    if (lIndex == NULL) 
	return (Datum) array_ref(array_scanner, i, upper.indx,
				 arrayRef->refelembyval,
				 arrayRef->refelemlength,
				 arrayRef->refattrlength, isNull);
    return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx,
			      arrayRef->refelembyval,
			      arrayRef->refelemlength, isNull);
}


/* ----------------------------------------------------------------
 *    	ExecEvalAggreg
 *    
 *    	Returns a Datum whose value is the value of the precomputed
 *	aggregate found in the given expression context.
 * ----------------------------------------------------------------
 */
static Datum
ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull)
{
    
    *isNull = econtext->ecxt_nulls[agg->aggno];
    return econtext->ecxt_values[agg->aggno];
}

/* ----------------------------------------------------------------
 *    	ExecEvalVar
 *    
 *    	Returns a Datum whose value is the value of a range
 *	variable with respect to given expression context.
 *
 *
 *      As an entry condition, we expect that the the datatype the
 *      plan expects to get (as told by our "variable" argument) is in
 *      fact the datatype of the attribute the plan says to fetch (as
 *      seen in the current context, identified by our "econtext"
 *      argument).
 * 
 *      If we fetch a Type A attribute and Caller treats it as if it
 *      were Type B, there will be undefined results (e.g. crash).
 *      One way these might mismatch now is that we're accessing a
 *      catalog class and the type information in the pg_attribute
 *      class does not match the hardcoded pg_attribute information
 *      (in pg_attribute.h) for the class in question.
 *     
 *      We have an Assert to make sure this entry condition is met.
 * 
 * ---------------------------------------------------------------- */
Datum
ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
{
    Datum	    	result;
    TupleTableSlot  	*slot;
    AttrNumber 		attnum;
    HeapTuple		heapTuple;
    TupleDesc		tuple_type;
    Buffer 		buffer;
    bool		byval;
    int16   		len;
    
    /* ----------------
     *	get the slot we want
     * ----------------
     */
    switch(variable->varno) {
    case INNER: /* get the tuple from the inner node */
	slot = econtext->ecxt_innertuple;
	break;
	
    case OUTER: /* get the tuple from the outer node */
	slot = econtext->ecxt_outertuple;
	break;
	
    default: /* get the tuple from the relation being scanned */
	slot = econtext->ecxt_scantuple;
	break;
    } 
    
    /* ----------------
     *   extract tuple information from the slot
     * ----------------
     */
    heapTuple =  slot->val;
    tuple_type = slot->ttc_tupleDescriptor;
    buffer =	 slot->ttc_buffer;

    attnum =  	variable->varattno;

    /* (See prolog for explanation of this Assert) */
    Assert(attnum <= 0 ||
    	(attnum - 1 <= tuple_type->natts - 1 &&
     	 tuple_type->attrs[attnum-1] != NULL &&
         variable->vartype == tuple_type->attrs[attnum-1]->atttypid))
    
    /*
     * If the attribute number is invalid, then we are supposed to
     * return the entire tuple, we give back a whole slot so that
     * callers know what the tuple looks like.
     */
    if (attnum == InvalidAttrNumber)
	{
	    TupleTableSlot  *tempSlot;
	    TupleDesc td;
	    HeapTuple tup;

	    tempSlot = makeNode(TupleTableSlot);
	    tempSlot->ttc_shouldFree = false;
	    tempSlot->ttc_descIsNew = true;
	    tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
	    tempSlot->ttc_buffer = InvalidBuffer;
	    tempSlot->ttc_whichplan = -1;

	    tup = heap_copytuple(slot->val);
	    td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
	    
	    ExecSetSlotDescriptor(tempSlot, td);
	    
	    ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
	    return (Datum) tempSlot;
	}
    
    result =  (Datum)
      heap_getattr(heapTuple,  /* tuple containing attribute */
		   buffer,     /* buffer associated with tuple */
		   attnum,     /* attribute number of desired attribute */
		   tuple_type, /* tuple descriptor of tuple */
		   isNull);    /* return: is attribute null? */
    
    /* ----------------
     *	return null if att is null
     * ----------------
     */
    if (*isNull)
	return (Datum) NULL;
    
    /* ----------------
     *   get length and type information..
     *   ??? what should we do about variable length attributes
     *	 - variable length attributes have their length stored
     *     in the first 4 bytes of the memory pointed to by the
     *     returned value.. If we can determine that the type
     *     is a variable length type, we can do the right thing.
     *	   -cim 9/15/89
     * ----------------
     */
    if (attnum < 0) {
	/* ----------------
	 *  If this is a pseudo-att, we get the type and fake the length.
	 *  There ought to be a routine to return the real lengths, so
	 *  we'll mark this one ... XXX -mao
	 * ----------------
	 */
        len = 	heap_sysattrlen(attnum);  	/* XXX see -mao above */
	byval = heap_sysattrbyval(attnum);	/* XXX see -mao above */
    } else {
        len =   tuple_type->attrs[ attnum-1 ]->attlen;
	byval = tuple_type->attrs[ attnum-1 ]->attbyval ? true : false ;
    }
    
    execConstByVal = byval;
    execConstLen =   len;
    
    return result;
}

/* ----------------------------------------------------------------
 *      ExecEvalParam
 *
 *      Returns the value of a parameter.  A param node contains
 *	something like ($.name) and the expression context contains
 *	the current parameter bindings (name = "sam") (age = 34)...
 *	so our job is to replace the param node with the datum
 *	containing the appropriate information ("sam").
 *
 *	Q: if we have a parameter ($.foo) without a binding, i.e.
 *	   there is no (foo = xxx) in the parameter list info,
 *	   is this a fatal error or should this be a "not available"
 *	   (in which case we shoud return a Const node with the
 *	    isnull flag) ?  -cim 10/13/89
 *
 *      Minor modification: Param nodes now have an extra field,
 *      `paramkind' which specifies the type of parameter 
 *      (see params.h). So while searching the paramList for
 *      a paramname/value pair, we have also to check for `kind'.
 *     
 *      NOTE: The last entry in `paramList' is always an
 *      entry with kind == PARAM_INVALID.
 * ----------------------------------------------------------------
 */
Datum
ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
{
    
    char  	  	*thisParameterName;
    int		  	thisParameterKind;
    AttrNumber		thisParameterId;
    int 	  	matchFound;
    ParamListInfo	paramList;
    
    thisParameterName = expression->paramname;
    thisParameterKind = expression->paramkind;
    thisParameterId =   expression->paramid;
    paramList = 	econtext->ecxt_param_list_info;
    
    *isNull = false;
    /*
     * search the list with the parameter info to find a matching name.
     * An entry with an InvalidName denotes the last element in the array.
     */
    matchFound = 0;
    if (paramList != NULL) {
	/*
	 * search for an entry in 'paramList' that matches
	 * the `expression'.
	 */
	while(paramList->kind != PARAM_INVALID && !matchFound) {
	    switch (thisParameterKind) {
	    case PARAM_NAMED:
		if (thisParameterKind == paramList->kind &&
		    strcmp(paramList->name, thisParameterName) == 0){
		    matchFound = 1;
		} 
		break;
	    case PARAM_NUM:
		if (thisParameterKind == paramList->kind &&
		    paramList->id == thisParameterId) {
		    matchFound = 1;
		}
		break;
	    case PARAM_OLD:
	    case PARAM_NEW:
		if (thisParameterKind == paramList->kind &&
		    paramList->id == thisParameterId)
		    {
			matchFound = 1;
			/*
			 * sanity check
			 */
		        if (strcmp(paramList->name, thisParameterName) != 0){
			    elog(WARN,
				 "ExecEvalParam: new/old params with same id & diff names");
			}
		    }
		break;
	    default:
		/*
		 * oops! this is not supposed to happen!
		 */
		elog(WARN, "ExecEvalParam: invalid paramkind %d",
		     thisParameterKind);
	    }
	    if (! matchFound) {
		paramList++;
	    }
	} /*while*/
    } /*if*/
    
    if (!matchFound) {
	/*
	 * ooops! we couldn't find this parameter
	 * in the parameter list. Signal an error
	 */
	elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
	     thisParameterName);
    }
    
    /*
     * return the value.
     */
    if (paramList->isnull)
	{
	    *isNull = true;
	    return (Datum)NULL;
	}
    
    if (expression->param_tlist != NIL)
	{
	    HeapTuple      tup;
	    Datum          value;
	    List           *tlist = expression->param_tlist;
	    TargetEntry	   *tle = (TargetEntry *)lfirst(tlist);
	    TupleTableSlot *slot = (TupleTableSlot *)paramList->value;
	    
	    tup = slot->val;
	    value = ProjectAttribute(slot->ttc_tupleDescriptor,
				     tle, tup, isNull);
	    return value;
	}
    return(paramList->value);
}


/* ----------------------------------------------------------------
 *    	ExecEvalOper / ExecEvalFunc support routines
 * ----------------------------------------------------------------
 */

/* ----------------
 *	GetAttributeByName
 *	GetAttributeByNum
 *
 *	These are functions which return the value of the
 *	named attribute out of the tuple from the arg slot.  User defined
 *	C functions which take a tuple as an argument are expected
 *	to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
 * ----------------
 */
char *
GetAttributeByNum(TupleTableSlot  *slot,
		  AttrNumber attrno,
		  bool *isNull)
{
    Datum retval;
    
    if (!AttributeNumberIsValid(attrno))
	elog(WARN, "GetAttributeByNum: Invalid attribute number");
    
    if (!AttrNumberIsForUserDefinedAttr(attrno))
	elog(WARN, "GetAttributeByNum: cannot access system attributes here");
    
    if (isNull == (bool *)NULL)
	elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed");
    
    if (TupIsNull(slot))
	{
	    *isNull = true;
	    return (char *) NULL;
	}
    
    retval = (Datum)
	heap_getattr(slot->val,
		     slot->ttc_buffer,
		     attrno,
		     slot->ttc_tupleDescriptor,
		     isNull);
    if (*isNull)
	return (char *) NULL;
    return (char *) retval;
}

/* XXX char16 name for catalogs */
char *
att_by_num(TupleTableSlot *slot,
	   AttrNumber attrno,
	   bool *isNull)
{
    return(GetAttributeByNum(slot, attrno, isNull));
}

char *
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
{
    AttrNumber attrno;
    TupleDesc tupdesc;
    HeapTuple tuple;
    Datum retval;
    int natts;
    int i;
    
    if (attname == NULL)
	elog(WARN, "GetAttributeByName: Invalid attribute name");
    
    if (isNull == (bool *)NULL)
	elog(WARN, "GetAttributeByName: a NULL isNull flag was passed");
    
    if (TupIsNull(slot))
	{
	    *isNull = true;
	    return (char *) NULL;
	}
    
    tupdesc = slot->ttc_tupleDescriptor;
    tuple = slot->val;
    
    natts = tuple->t_natts;
    
    attrno = InvalidAttrNumber;
    for (i=0;i<tupdesc->natts;i++) {
	if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) {
	    attrno = tupdesc->attrs[i]->attnum;
	    break;
	}
    }

    if (attrno == InvalidAttrNumber)
	elog(WARN, "GetAttributeByName: attribute %s not found", attname);
    
    retval = (Datum)
	heap_getattr(slot->val,
		     slot->ttc_buffer,
		     attrno,
		     tupdesc,
		     isNull);
    if (*isNull)
	return (char *) NULL;
    return (char *) retval;
}

/* XXX char16 name for catalogs */
char *
att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
{
    return(GetAttributeByName(slot, attname, isNull));
}

void
ExecEvalFuncArgs(FunctionCachePtr fcache,
		 ExprContext *econtext,
		 List *argList,
		 Datum argV[],
		 bool *argIsDone)
{
    int i;
    bool argIsNull, *nullVect;
    List *arg;
    
    nullVect = fcache->nullVect;
    
    i = 0;
    foreach (arg, argList) {
	/* ----------------
	 *   evaluate the expression, in general functions cannot take
	 *   sets as arguments but we make an exception in the case of
	 *   nested dot expressions.  We have to watch out for this case
	 *   here.
	 * ----------------
	 */
	argV[i] = (Datum)
	    ExecEvalExpr((Node *) lfirst(arg),
			 econtext,
			 &argIsNull,
			 argIsDone);
	if (! (*argIsDone))
	    {
		Assert(i == 0);
		fcache->setArg = (char *)argV[0];
		fcache->hasSetArg = true;
	    }
	if (argIsNull)
	    nullVect[i] = true;
	else
	    nullVect[i] = false;
	i++;
    }
}

/* ----------------
 *	ExecMakeFunctionResult
 * ----------------
 */
Datum
ExecMakeFunctionResult(Node *node,
		       List *arguments,
		       ExprContext *econtext,
		       bool *isNull,
		       bool *isDone)
{
    Datum	argv[MAXFMGRARGS];
    FunctionCachePtr fcache;
    Func	*funcNode = NULL;
    Oper	*operNode = NULL;
    bool        funcisset = false;
    
    /*
     * This is kind of ugly, Func nodes now have targetlists so that
     * we know when and what to project out from postquel function results.
     * This means we have to pass the func node all the way down instead
     * of using only the fcache struct as before.  ExecMakeFunctionResult
     * becomes a little bit more of a dual personality as a result.
     */
    if (IsA(node,Func))
	{
	    funcNode = (Func *)node;
	    fcache = funcNode->func_fcache;
	}
    else
	{
	    operNode = (Oper *)node;
	    fcache = operNode->op_fcache;
	}
    
    /* ----------------
     *	arguments is a list of expressions to evaluate
     *  before passing to the function manager.
     *  We collect the results of evaluating the expressions
     *  into a datum array (argv) and pass this array to arrayFmgr()
     * ----------------
     */
    if (fcache->nargs != 0) {
	bool argDone;
	
	if (fcache->nargs > MAXFMGRARGS) 
	    elog(WARN, "ExecMakeFunctionResult: too many arguments");
	
	/*
	 * If the setArg in the fcache is set we have an argument
	 * returning a set of tuples (i.e. a nested dot expression).  We
	 * don't want to evaluate the arguments again until the function
	 * is done. hasSetArg will always be false until we eval the args
	 * for the first time. We should set this in the parser.
	 */
	if ((fcache->hasSetArg) && fcache->setArg != NULL)
	    {
		argv[0] = (Datum)fcache->setArg;
		argDone = false;
	    }
	else
	    ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
	
	if ((fcache->hasSetArg) && (argDone)) {
	    if (isDone) *isDone = true;
	    return (Datum)NULL;
	}
    }
    
    /* If this function is really a set, we have to diddle with things.
     * If the function has already been called at least once, then the 
     * setArg field of the fcache holds
     * the OID of this set in pg_proc.  (This is not quite legit, since
     * the setArg field is really for functions which take sets of tuples
     * as input - set functions take no inputs at all.  But it's a nice
     * place to stash this value, for now.)
     *
     * If this is the first call of the set's function, then
     * the call to ExecEvalFuncArgs above just returned the OID of 
     * the pg_proc tuple which defines this set.  So replace the existing
     * funcid in the funcnode with the set's OID.  Also, we want a new
     * fcache which points to the right function, so get that, now that
     * we have the right OID.  Also zero out the argv, since the real
     * set doesn't take any arguments.
     */
    if (((Func *)node)->funcid == SetEvalRegProcedure) {
	funcisset = true;
	if (fcache->setArg) {
	    argv[0] = 0;

	    ((Func *)node)->funcid = (Oid) PointerGetDatum(fcache->setArg);
		       
	} else {
	    ((Func *)node)->funcid = (Oid) argv[0];
	    setFcache(node, argv[0], NIL,econtext);
	    fcache = ((Func *)node)->func_fcache;
	    fcache->setArg = (char*)argv[0];
	    argv[0] = (Datum)0;
	}
    }
    
    /* ----------------
     *   now return the value gotten by calling the function manager, 
     *   passing the function the evaluated parameter values. 
     * ----------------
     */
    if (fcache->language == SQLlanguageId) {
	Datum result;
	
	Assert(funcNode);
	result = postquel_function (funcNode, (char **) argv, isNull, isDone);
	/*
	 * finagle the situation where we are iterating through all results
	 * in a nested dot function (whose argument function returns a set
	 * of tuples) and the current function finally finishes.  We need to
	 * get the next argument in the set and run the function all over
	 * again.  This is getting unclean.
	 */
	if ((*isDone) && (fcache->hasSetArg)) {
	    bool argDone;
	    
	    ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
	    
	    if (argDone) {
	        fcache->setArg = (char *)NULL;
		*isDone = true;
		result = (Datum)NULL;
	    }
	    else
		result = postquel_function(funcNode,
					   (char **) argv,
					   isNull,
					   isDone);
	}
	if (funcisset) {
	    /* reset the funcid so that next call to this routine will
	     * still recognize this func as a set.
	     * Note that for now we assume that the set function in
	     * pg_proc must be a Postquel function - the funcid is
	     * not reset below for C functions.
	     */
	    ((Func *)node)->funcid = SetEvalRegProcedure;
	    /* If we're done with the results of this function, get rid
	     * of its func cache.
	     */
	    if (*isDone) {
		((Func *)node)->func_fcache = NULL;
	    }
	}
	return result;
    }
    else 
	{
	    int i;
	    
	    if (isDone) *isDone = true;
	    for (i = 0; i < fcache->nargs; i++) 
		if (fcache->nullVect[i] == true) *isNull = true;
	    
	    return((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs,
				  (FmgrValues *) argv, isNull));
	}
}


/* ----------------------------------------------------------------
 *    	ExecEvalOper
 *    	ExecEvalFunc
 *    
 *    	Evaluate the functional result of a list of arguments by calling the
 *    	function manager.  Note that in the case of operator expressions, the
 *    	optimizer had better have already replaced the operator OID with the
 *    	appropriate function OID or we're hosed.
 *
 * old comments
 *    	Presumably the function manager will not take null arguments, so we
 *    	check for null arguments before sending the arguments to (fmgr).
 *    
 *    	Returns the value of the functional expression.
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *	ExecEvalOper
 * ----------------------------------------------------------------
 */
Datum
ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
{
    Oper	*op; 
    List	*argList;
    FunctionCachePtr fcache;
    bool	isDone;

    /* ----------------
     *  an opclause is a list (op args).  (I think)
     *
     *  we extract the oid of the function associated with
     *  the op and then pass the work onto ExecMakeFunctionResult
     *  which evaluates the arguments and returns the result of
     *  calling the function on the evaluated arguments.
     * ----------------
     */
    op = 	(Oper *) opClause->oper;
    argList =   opClause->args;
    
    /*
     * get the fcache from the Oper node.
     * If it is NULL, then initialize it
     */
    fcache = op->op_fcache;
    if (fcache == NULL) {
        setFcache((Node*)op, op->opid, argList, econtext);
    	fcache = op->op_fcache;
    }
    
    /* -----------
     *  call ExecMakeFunctionResult() with a dummy isDone that we ignore.
     *  We don't have operator whose arguments are sets.
     * -----------
     */
    return
	ExecMakeFunctionResult((Node *)op, argList, econtext, isNull, &isDone);
}

/* ----------------------------------------------------------------
 *	ExecEvalFunc
 * ----------------------------------------------------------------
 */

Datum
ExecEvalFunc(Expr *funcClause,
	     ExprContext *econtext,
	     bool *isNull,
	     bool *isDone)
{
    Func	*func;
    List	*argList;
    FunctionCachePtr fcache;
    
    /* ----------------
     *  an funcclause is a list (func args).  (I think)
     *
     *  we extract the oid of the function associated with
     *  the func node and then pass the work onto ExecMakeFunctionResult
     *  which evaluates the arguments and returns the result of
     *  calling the function on the evaluated arguments.
     *
     *  this is nearly identical to the ExecEvalOper code.
     * ----------------
     */
    func =	(Func *)funcClause->oper;
    argList =   funcClause->args;
    
    /*
     * get the fcache from the Func node.
     * If it is NULL, then initialize it
     */
    fcache = func->func_fcache;
    if (fcache == NULL) {
    	setFcache((Node*)func, func->funcid, argList, econtext);
    	fcache = func->func_fcache;
    }
    
    return
	ExecMakeFunctionResult((Node*)func, argList, econtext, isNull, isDone);
}

/* ----------------------------------------------------------------
 *    	ExecEvalNot
 *    	ExecEvalOr
 *	ExecEvalAnd
 *    
 *    	Evaluate boolean expressions.  Evaluation of 'or' is
 *	short-circuited when the first true (or null) value is found.
 *
 *	The query planner reformulates clause expressions in the
 *	qualification to conjunctive normal form.  If we ever get
 *	an AND to evaluate, we can be sure that it's not a top-level
 *	clause in the qualification, but appears lower (as a function
 *	argument, for example), or in the target list.  Not that you
 *	need to know this, mind you...
 * ----------------------------------------------------------------
 */
Datum
ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
{
    Datum expr_value;
    Node *clause;
    bool  isDone;
    
    clause = lfirst(notclause->args);
    
    /* ----------------
     *  We don't iterate over sets in the quals, so pass in an isDone
     *  flag, but ignore it.
     * ----------------
     */
    expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
    
    /* ----------------
     *	if the expression evaluates to null, then we just
     *  cascade the null back to whoever called us.
     * ----------------
     */
    if (*isNull)
	return expr_value;
    
    /* ----------------
     *  evaluation of 'not' is simple.. expr is false, then
     *  return 'true' and vice versa.
     * ----------------
     */
    if (DatumGetInt32(expr_value) == 0)
	return (Datum) true;
    
    return (Datum) false;
}

/* ----------------------------------------------------------------
 *	ExecEvalOr
 * ----------------------------------------------------------------
 */
Datum
ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
{
    List   *clauses;
    List   *clause;
    bool   isDone;
    bool IsNull;
    Datum  const_value = 0;
    
    IsNull = false; 
    clauses = orExpr->args;
    
    /* ----------------
     *  we use three valued logic functions here...
     *	we evaluate each of the clauses in turn,
     *  as soon as one is true we return that
     *  value.  If none is true and  none of the
     *  clauses evaluate to NULL we return
     *  the value of the last clause evaluated (which
     *  should be false) with *isNull set to false else 
     *  if none is true and at least one clause evaluated
     *  to NULL we set *isNull flag to true -  
     * ----------------
     */
    foreach (clause, clauses) {
	
	/* ----------------
	 *  We don't iterate over sets in the quals, so pass in an isDone
	 *  flag, but ignore it.
	 * ----------------
	 */
	const_value = ExecEvalExpr((Node *) lfirst(clause),
				   econtext,
				   isNull,
				   &isDone);
	
	/* ----------------
	 *  if the expression evaluates to null, then we 
	 *  remember it in the local IsNull flag, if none of the
	 *  clauses are true then we need to set *isNull
	 *  to true again.
	 * ----------------
	 */
	if (*isNull)
	    IsNull = *isNull;
	
	/* ----------------
	 *   if we have a true result, then we return it.
	 * ----------------
	 */
	if (DatumGetInt32(const_value) != 0)
	    return const_value;
    }
    
    /* IsNull is true if at least one clause evaluated to NULL */	
    *isNull = IsNull;
    return const_value;
}

/* ----------------------------------------------------------------
 *	ExecEvalAnd
 * ----------------------------------------------------------------
 */
Datum
ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
{
    List   *clauses;
    List   *clause;
    Datum  const_value = 0;
    bool   isDone;
    bool IsNull;
    
    IsNull = false;
    
    clauses = andExpr->args;
    
    /* ----------------
     *	we evaluate each of the clauses in turn,
     *  as soon as one is false we return that
     *  value.  If none are false or NULL then we return
     *  the value of the last clause evaluated, which
     *  should be true. 
     * ----------------
     */
    foreach (clause, clauses) {
	
	/* ----------------
	 *  We don't iterate over sets in the quals, so pass in an isDone
	 *  flag, but ignore it.
	 * ----------------
	 */
	const_value = ExecEvalExpr((Node *) lfirst(clause),
				   econtext,
				   isNull,
				   &isDone);
	
	/* ----------------
	 *  if the expression evaluates to null, then we 
	 *  remember it in IsNull, if none of the clauses after
	 *  this evaluates to false we will have to set *isNull
	 *  to true again.
	 * ----------------
	 */
	if (*isNull)
	    IsNull = *isNull;
	
	/* ----------------
	 *   if we have a false result, then we return it, since the
	 *   conjunction must be false.
	 * ----------------
	 */
	if (DatumGetInt32(const_value) == 0)
	    return const_value;
    }
    
    *isNull = IsNull;
    return const_value;
}

/* ----------------------------------------------------------------  
 *    	ExecEvalExpr
 *    
 *    	Recursively evaluate a targetlist or qualification expression.
 *
 *    	This routine is an inner loop routine and should be as fast
 *      as possible.
 *
 *      Node comparison functions were replaced by macros for speed and to plug
 *      memory leaks incurred by using the planner's Lispy stuff for
 *      comparisons.  Order of evaluation of node comparisons IS IMPORTANT;
 *      the macros do no checks.  Order of evaluation:
 *      
 *      o an isnull check, largely to avoid coredumps since greg doubts this
 *        routine is called with a null ptr anyway in proper operation, but is
 *        not completely sure...
 *      o ExactNodeType checks.
 *      o clause checks or other checks where we look at the lfirst of something.
 * ----------------------------------------------------------------
 */
Datum
ExecEvalExpr(Node *expression,
	     ExprContext *econtext,
	     bool *isNull,
	     bool *isDone)
{
    Datum retDatum = 0;
    
    *isNull = false;
    
    /*
     * Some callers don't care about is done and only want 1 result.  They
     * indicate this by passing NULL
     */
    if (isDone)
	*isDone = true;
    
    /* ----------------
     *	here we dispatch the work to the appropriate type
     *  of function given the type of our expression.
     * ----------------
     */
    if (expression == NULL) {
	*isNull = true;
	return (Datum) true;
    }

    switch(nodeTag(expression)) {
    case T_Var:
	retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
	break;
    case T_Const: {
	Const *con = (Const *)expression;

	if (con->constisnull)
	    *isNull = true;
    	retDatum = con->constvalue;
	break;
    }
    case T_Param:
	retDatum = (Datum)ExecEvalParam((Param *)expression, econtext, isNull);
	break;
    case T_Iter:
	retDatum = (Datum)  ExecEvalIter((Iter *) expression,
					 econtext,
					 isNull,
					 isDone);
	break;
    case T_Aggreg:
	retDatum = (Datum) ExecEvalAggreg((Aggreg *)expression,
					  econtext,
					  isNull);
	break;
    case T_ArrayRef:
	retDatum = (Datum)  ExecEvalArrayRef((ArrayRef *) expression,
					     econtext,
					     isNull,
					     isDone);
	break;
    case T_Expr: {
	Expr *expr = (Expr *)expression;
	switch (expr->opType) {
	case OP_EXPR:
	    retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
	    break;
	case FUNC_EXPR:
	    retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
	    break;
	case OR_EXPR:
	    retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
	    break;
	case AND_EXPR:
	    retDatum = (Datum)  ExecEvalAnd(expr, econtext, isNull);
	    break;
	case NOT_EXPR:
	    retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
	    break;
	default:
	    elog(WARN, "ExecEvalExpr: unknown expression type");
	    break;
	}
	break;
    }
    default:
	elog(WARN, "ExecEvalExpr: unknown expression type");
	break;
    }
    
    return retDatum;
}

/* ----------------------------------------------------------------
 *		     ExecQual / ExecTargetList 
 * ----------------------------------------------------------------
 */

/* ----------------------------------------------------------------
 *    	ExecQualClause
 *
 *	this is a workhorse for ExecQual.  ExecQual has to deal
 *	with a list of qualifications, so it passes each qualification
 *	in the list to this function one at a time.  ExecQualClause
 *	returns true when the qualification *fails* and false if
 *	the qualification succeeded (meaning we have to test the
 *	rest of the qualification)
 * ----------------------------------------------------------------
 */
bool
ExecQualClause(Node *clause, ExprContext *econtext)
{
    Datum   expr_value;
    bool isNull;
    bool isDone;
    
    /* when there is a null clause, consider the qualification to be true */
    if (clause == NULL)
      return true;

    /*
     * pass isDone, but ignore it.  We don't iterate over multiple
     * returns in the qualifications.
     */
    expr_value = (Datum)
	ExecEvalExpr(clause, econtext, &isNull, &isDone);
    
    /* ----------------
     *	this is interesting behaviour here.  When a clause evaluates
     *  to null, then we consider this as passing the qualification.
     *  it seems kind of like, if the qual is NULL, then there's no
     *  qual.. 
     * ----------------
     */
    if (isNull)
	return true;
    
    /* ----------------
     *  remember, we return true when the qualification fails..
     * ----------------
     */
    if (DatumGetInt32(expr_value) == 0)
	return true;
    
    return false;
}

/* ----------------------------------------------------------------
 *    	ExecQual
 *    
 *    	Evaluates a conjunctive boolean expression and returns t
 *	iff none of the subexpressions are false (or null).
 * ----------------------------------------------------------------
 */
bool
ExecQual(List *qual, ExprContext *econtext)
{
    List 		*clause;
    bool 		result;
    
    /* ----------------
     *	debugging stuff
     * ----------------
     */
    EV_printf("ExecQual: qual is ");
    EV_nodeDisplay(qual);
    EV_printf("\n");
    
    IncrProcessed();
    
    /* ----------------
     *	return true immediately if no qual
     * ----------------
     */
    if (qual == NIL)
	return true;
    
    /* ----------------
     *  a "qual" is a list of clauses.  To evaluate the
     *  qual, we evaluate each of the clauses in the list.
     *
     *  ExecQualClause returns true when we know the qualification
     *  *failed* so we just pass each clause in qual to it until
     *  we know the qual failed or there are no more clauses.
     * ----------------
     */
    result = false;
    foreach (clause, qual) {
	result = ExecQualClause((Node *)lfirst(clause), econtext);
	if (result == true)
	    break;
    }
    
    /* ----------------
     *	if result is true, then it means a clause failed so we
     *  return false.  if result is false then it means no clause
     *  failed so we return true.
     * ----------------
     */
    if (result == true)
	return false;
    
    return true;
}

int
ExecTargetListLength(List *targetlist)
{
    int len;
    List *tl;
    TargetEntry *curTle;
    
    len = 0;
    foreach (tl, targetlist) {
	curTle = lfirst(tl);
	
	if (curTle->resdom != NULL)
	    len++;
	else
	    len += curTle->fjoin->fj_nNodes;
    }
    return len;
}

/* ----------------------------------------------------------------
 *    	ExecTargetList
 *    
 *    	Evaluates a targetlist with respect to the current
 *	expression context and return a tuple.
 * ----------------------------------------------------------------
 */
static HeapTuple
ExecTargetList(List *targetlist,
	       int nodomains, 
	       TupleDesc targettype,
	       Datum *values,
	       ExprContext *econtext,
	       bool *isDone)
{
    char		nulls_array[64];
    bool		fjNullArray[64];
    bool		*fjIsNull;
    char		*null_head;
    List 		*tl;
    TargetEntry		*tle;
    Node 		*expr;
    Resdom    	  	*resdom;
    AttrNumber		resind;
    Datum 		constvalue;
    HeapTuple 		newTuple;
    bool 		isNull;
    
    /* ----------------
     *	debugging stuff
     * ----------------
     */
    EV_printf("ExecTargetList: tl is ");
    EV_nodeDisplay(targetlist);
    EV_printf("\n");
    
    /* ----------------
     * Return a dummy tuple if the targetlist is empty .
     * the dummy tuple is necessary to differentiate 
     * between passing and failing the qualification.
     * ----------------
     */
    if (targetlist == NIL) {
	/* ----------------
	 *	I now think that the only time this makes
	 *	any sence is when we run a delete query.  Then
	 *	we need to return something other than nil
	 *	so we know to delete the tuple associated
	 *      with the saved tupleid.. see what ExecutePlan
	 *      does with the returned tuple.. -cim 9/21/89
	 *
	 *      It could also happen in queries like:
	 *	    retrieve (foo.all) where bar.a = 3
	 *
	 *      is this a new phenomenon? it might cause bogus behavior
	 *      if we try to free this tuple later!! I put a hook in
	 *      ExecProject to watch out for this case -mer 24 Aug 1992
	 * ----------------
	 */
	CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext);
	*isDone = true;
	return (HeapTuple) true;
    }
    
    /* ----------------
     *  allocate an array of char's to hold the "null" information
     *  only if we have a really large targetlist.  otherwise we use
     *  the stack.
     * ----------------
     */
    if (nodomains > 64) {
        null_head = (char *) palloc(nodomains+1);
	fjIsNull = (bool *) palloc(nodomains+1);
    } else {
	null_head = &nulls_array[0];
	fjIsNull = &fjNullArray[0];
    }
    
    /* ----------------
     *	evaluate all the expressions in the target list
     * ----------------
     */
    EV_printf("ExecTargetList: setting target list values\n");
    
    *isDone = true;
    foreach (tl, targetlist) {
	/* ----------------
	 *    remember, a target list is a list of lists:
	 *
	 *	((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
	 *
	 *    tl is a pointer to successive cdr's of the targetlist
	 *    tle is a pointer to the target list entry in tl
	 * ----------------
	 */
	tle = lfirst(tl);

	if (tle->resdom != NULL) {
	    expr       = tle->expr;
	    resdom     = tle->resdom;
	    resind     = resdom->resno - 1;
	    constvalue = (Datum) ExecEvalExpr(expr,
					      econtext,
					      &isNull,
					      isDone);
	    
	    if ((IsA(expr,Iter)) && (*isDone))
		return (HeapTuple)NULL;
	    
	    values[resind] = constvalue;
	    
	    if (!isNull)
		null_head[resind] = ' ';
	    else
		null_head[resind] = 'n';
	}else {
	    int      curNode;
	    Resdom   *fjRes;
	    List     *fjTlist   = (List *)tle->expr;
	    Fjoin    *fjNode    = tle->fjoin;
 	    int      nNodes    = fjNode->fj_nNodes;
	    DatumPtr results   = fjNode->fj_results;
	    
	    ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
	    if (*isDone)
		return (HeapTuple)NULL;
	    
	    /*
	     * get the result from the inner node
	     */
	    fjRes = (Resdom *)fjNode->fj_innerNode;
	    resind = fjRes->resno - 1;
	    if (fjIsNull[0])
		null_head[resind] = 'n';
	    else {
		null_head[resind] = ' ';
		values[resind] = results[0];
	    }
	    
	    /*
	     * Get results from all of the outer nodes
	     */
	    for (curNode = 1;
		 curNode < nNodes;
		 curNode++, fjTlist = lnext(fjTlist))
		{
#if 0 /* what is this?? */
		    Node *outernode = lfirst(fjTlist);
		    fjRes = (Resdom *)outernode->iterexpr;
#endif		    
		    resind = fjRes->resno - 1;
		    if (fjIsNull[curNode]) {
			null_head[resind] = 'n';
		    }else {
			null_head[resind] = ' ';
			values[resind] = results[curNode];
		    }
		}
	}
    }
    
    /* ----------------
     * 	form the new result tuple (in the "normal" context)
     * ----------------
     */
    newTuple = (HeapTuple)
	heap_formtuple(targettype, values, null_head);
    
    /* ----------------
     *	free the nulls array if we allocated one..
     * ----------------
     */
    if (nodomains > 64) pfree(null_head);
    
    return
	newTuple;
}

/* ----------------------------------------------------------------
 *    	ExecProject
 *    
 *	projects a tuple based in projection info and stores
 *	it in the specified tuple table slot.
 *
 *	Note: someday soon the executor can be extended to eliminate
 *	      redundant projections by storing pointers to datums
 *	      in the tuple table and then passing these around when
 *	      possible.  this should make things much quicker.
 *	      -cim 6/3/91
 * ----------------------------------------------------------------
 */
TupleTableSlot *
ExecProject(ProjectionInfo *projInfo, bool *isDone)
{
    TupleTableSlot	*slot;
    List 	 	*targetlist;
    int 	 	len;
    TupleDesc	 	tupType;
    Datum 	 	*tupValue;
    ExprContext	 	*econtext;
    HeapTuple		newTuple;
    
    /* ----------------
     *	sanity checks
     * ----------------
     */
    if (projInfo == NULL)
	return (TupleTableSlot *) NULL;

    /* ----------------
     *  get the projection info we want
     * ----------------
     */
    slot = 		projInfo->pi_slot;
    targetlist = 	projInfo->pi_targetlist;
    len = 		projInfo->pi_len;
    tupType = 		slot->ttc_tupleDescriptor;
    
    tupValue = 		projInfo->pi_tupValue;
    econtext = 		projInfo->pi_exprContext;
    
    if (targetlist == NIL) {
      *isDone = true;
      return (TupleTableSlot *) NULL;
    }

    /* ----------------
     *  form a new (result) tuple
     * ----------------
     */
    newTuple = ExecTargetList(targetlist,
			      len,
			      tupType,
			      tupValue,
			      econtext,
			      isDone);
    
    /* ----------------
     *	store the tuple in the projection slot and return the slot.
     *
     *  If there's no projection target list we don't want to pfree
     *  the bogus tuple that ExecTargetList passes back to us.
     *       -mer 24 Aug 1992
     * ----------------
     */
    return (TupleTableSlot *)
	ExecStoreTuple(newTuple, /* tuple to store */
		       slot,     /* slot to store in */
		       InvalidBuffer, 	   /* tuple has no buffer */
		       true);
}

